package mtm.ui.resizable;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;

import mtm.constants.Constants;
import mtm.date.MtmDate;
import mtm.db.DatabaseHandler;
import mtm.db.Job;
import mtm.db.RepeatJob;
import mtm.ui.job.JobFrame;
import mtm.ui.job.RepeatJobFrame;
import mtm.ui.panel.WeekPanel;

import org.w3c.dom.css.Rect;

/**
 * Resizable label
 */
public class ResizableLabel extends JLabel {

	private boolean isSelected = true;		// check this label is selected or not
	private boolean isIntersected = false;	// check this label is interseced with another label
	
	private Job job;

	/**
	 * Constructor
	 */
	public ResizableLabel(Job job) {
		this(new ResizableBorder(6), job);
		
		addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				// show information of job when double click
				if (e.getClickCount() == 2) {
					processDoubleClickEvent();
				}
			}
		});
	}
	
	
	/**
	 * Process double click event on label
	 */
	private void processDoubleClickEvent() {
		if (job.isGeneratedJob()) {
			RepeatJob repeatJob = DatabaseHandler.getInstance().getRepeatJob(job.getParentId());
			new RepeatJobFrame(repeatJob);
		} else {
			new JobFrame(job);
		}
	}

	
	/**
	 * Constructor with initial border and a Job object for this label
	 * to display
	 * 
	 * @param border
	 * @param job
	 */
	public ResizableLabel(ResizableBorder border, Job job) {
		this.job = job;
		
		// to display background color when add to a panel or frame
		setOpaque(true);	
		
		// set icon
		if (job.getIcon() != null) {
			Image icon = null;
			try {
				icon = Toolkit.getDefaultToolkit().createImage(
						new URL(job.getIcon()));
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
			setIcon(new ImageIcon(
				icon.getScaledInstance(
					Constants.JOB_ICON_WIDTH, 
					Constants.JOB_ICON_HEIGHT,
					Image.SCALE_SMOOTH), 
				"abc"));
		}
		
//		// foreground color is blue
//		setForeground(Color.blue);
		
		// set background
		setBackground( new Color(job.getColor()) );
		
		// label's text
		String start = job.getStartTime().getTimeFormat();
		String end = job.getEndTime().getTimeFormat();
		setFont(new Font(Font.SERIF, Font.ITALIC, 10));
		setText("<html>" + start + " - " + end + "<br />" + job.getJobName() + "</html>");
		
		// process mouse
		addMouseListener(resizeListener);
		addMouseMotionListener(resizeListener);
		
		// other settings
		setBorder(border);
		setToolTipText(job.getJobName());
		setVerticalAlignment(JLabel.TOP);
	}
	
	
	/**
	 * Return Id of the Job object inside this label
	 * @return
	 */
	public int getId() {
		return job.getId();
	}
	
	
	/**
	 * Return Job object of this label
	 * @return
	 */
	public Job getJob() {
		return job;
	}

	
	/**
	 * Check this label is selected or not
	 * 
	 * @return
	 */
	public boolean isSelected() {
		return isSelected;
	}
	
	
	/**
	 * Check this label is intersected with another label or not
	 * @return
	 */
	public boolean isIntersected() {
		return isIntersected;
	}

	
	/**
	 * Update UI of this label when resize
	 */
	private void updateWeekPanel() {
		if (getParent() != null) {
			((JComponent) getParent()).revalidate();
			// TODO process x, y and h of label
			// System.out.println("Process resize , y = " + getY() + ", h = " +
			// getHeight());
		}
	}
	
	
	/**
	 * Change display text of this label based on its location on screen
	 * and update information of job in DB
	 */
	private void changeDisplayText() {
		Rectangle bound = getBounds();
		
		// start time
		int date = 
			(bound.x - WeekPanel.BORDER_WIDTH) / WeekPanel.WIDTH_OF_CELL + WeekPanel.getInstance().getFirstDayOfWeek().getDate();
		int hour = (bound.y - WeekPanel.BORDER_HEIGHT) / WeekPanel.HEIGHT_OF_CELL;
		int time = (bound.y - WeekPanel.BORDER_HEIGHT) % WeekPanel.HEIGHT_OF_CELL == 0 ? 0 : 30;
		job.getStartTime().setHours(hour);
		job.getStartTime().setMinutes(time);
		job.getStartTime().setDate(date);
		String startTime = job.getStartTime().getTimeFormat();
		
		// end time
		hour = (bound.y + bound.height - WeekPanel.BORDER_HEIGHT) / WeekPanel.HEIGHT_OF_CELL;
		time = (bound.y + bound.height - WeekPanel.BORDER_HEIGHT) % WeekPanel.HEIGHT_OF_CELL == 0 ? 0 : 30;
		if (hour == 24) {
			job.getEndTime().setHours(23);
			job.getEndTime().setMinutes(59);
		} else {
			job.getEndTime().setHours(hour);
			job.getEndTime().setMinutes(time);
		}
		job.getEndTime().setDate(date);
		
		// set text
		String endTime = job.getEndTime().getTimeFormat();
		// set text
		setText("<html>" + startTime + " - " + endTime + "<br />" + job.getJobName() + "</html>");
		
		// update job in DB
		DatabaseHandler.getInstance().updateJob(job, job.getId());
	}
	
	
	
	private int round(int x, int n) {
		return (x / n) * n;
	}
	

	/**
	 * Process mouse event
	 */
	MouseInputListener resizeListener = new MouseInputAdapter() {
		
		private int cursor;
		private Point dragPosition = null;		// point where the mouse is dragging
		private Point oldPosition = null;		// point where the mouse start drag
		private Rectangle bound;
		
		private void moveToOldLocation() {
			bound = getBounds();
			
			int dx = oldPosition.x - getX();
			int dy = oldPosition.y - getY();
			bound.translate(dx, dy);
		}

		public void mouseMoved(MouseEvent me) {
			ResizableBorder border = (ResizableBorder) getBorder();
			setCursor(Cursor.getPredefinedCursor(border.getCursor(me)));
		}

		public void mouseExited(MouseEvent mouseEvent) {
			isSelected = false;
			setCursor(Cursor.getDefaultCursor());
		}

		public void mousePressed(MouseEvent me) {
			isSelected = true;
			ResizableBorder border = (ResizableBorder) getBorder();
			cursor = border.getCursor(me);
			dragPosition = me.getPoint();
			oldPosition = new Point(getX(), getY());
			requestFocus();
			repaint();
		}

		public void mouseDragged(MouseEvent me) {
			// don't allow job generated from repeat job moving
			if(job.getId() == 0) {
				return;
			}
			
			// drag label
			int x = getX();
			int y = getY();
			int w = getWidth();
			int h = getHeight();

			int dx = me.getX() - dragPosition.x;
			int dy = me.getY() - dragPosition.y;
			
			// process intersect event (a label intersecs with another when dragging)
			bound = getBounds();
			for (Rectangle rect : WeekPanel.getLabelsBound(bound)) {
				if (bound.intersects(rect)) {
					isIntersected = true;
					updateWeekPanel();
					break;
				}
			}

			// process resize event
			switch (cursor) {
			case Cursor.S_RESIZE_CURSOR:
				if (!(h + dy < WeekPanel.HEIGHT_OF_CELL / 2)) {
					setBounds(x, y, w, h + dy);
					dragPosition = me.getPoint();
					updateWeekPanel();
				}
				break;

			// case Cursor.N_RESIZE_CURSOR:
			// if (!(h - dy < 50)) {
			// setBounds(x, y + dy, w, h - dy);
			// resize();
			// }
			// break;

			case Cursor.HAND_CURSOR:
				Rectangle bounds = getBounds();
				bounds.translate(dx, dy);
				setBounds(bounds);
				updateWeekPanel();

			}

			setCursor(Cursor.getPredefinedCursor(cursor));
		}

		public void mouseReleased(MouseEvent me) {
			// check for start drag on a location which is not appropriate
//			if (dragPosition.x < WeekPanel.BORDER_WIDTH  
//					|| dragPosition.x > WeekPanel.BORDER_WIDTH + 7* WeekPanel.WIDTH_OF_CELL
//					|| dragPosition.y < WeekPanel.BORDER_HEIGHT
//					|| dragPosition.y > WeekPanel.BORDER_HEIGHT + 24 * WeekPanel.HEIGHT_OF_CELL) {
//				return;
//			}
			
			isSelected = false;
			
			int x = getX();
			int y = getY();
			int dx, dy;
			bound = getBounds();

			// calculate number of labels intersected with this label
			int intersectCount = 0;
			Rectangle intersectedBound = null;
			for (Rectangle rect : WeekPanel.getLabelsBound(bound)) {
				if (rect.intersects(bound)) {
					intersectedBound = rect;
					intersectCount++;
				}
			}
			
			// determine new location of this label
			switch (intersectCount) {
			// just move this label to an appropriate location
			case 0:
				x -= WeekPanel.BORDER_WIDTH;
				y -= WeekPanel.BORDER_HEIGHT;
				
				dx = round(x, WeekPanel.WIDTH_OF_CELL) - x;
				int dx2 = WeekPanel.WIDTH_OF_CELL + dx;
				dy = round(y, WeekPanel.HEIGHT_OF_CELL) - y;

				// translate label to new location
				if (-dx < dx2) {
					bound.translate(dx, dy);
				} else {
					bound.translate(dx2, dy);
				}
				
				// recalculate height
				bound.height = round(bound.height, WeekPanel.HEIGHT_OF_CELL / 2);
				break;
				
			// intersect with 1 label
			case 1:
				// move to old location when not enough space to place this label
				// otherwise, move to an appropriate location
				boolean isAbove = y < intersectedBound.y && x - intersectedBound.x < WeekPanel.WIDTH_OF_CELL / 2;
				if (isAbove) {
					if (intersectedBound.y - WeekPanel.BORDER_HEIGHT < bound.height) {
						moveToOldLocation();
					} else {
						bound.y = intersectedBound.y - bound.height;
						bound.x = intersectedBound.x;
					}
				}
				
				boolean isBelow = y > intersectedBound.y && x - intersectedBound.x < WeekPanel.WIDTH_OF_CELL / 2;
				if (isBelow) {
					if (24 * WeekPanel.HEIGHT_OF_CELL - intersectedBound.height - intersectedBound.y < bound.height) {
						moveToOldLocation();
					} else {
						bound.y = intersectedBound.y + intersectedBound.height;
						bound.x = intersectedBound.x;
					}
				}
				
				boolean isLeft = x - intersectedBound.x < 0 && intersectedBound.x - x > WeekPanel.WIDTH_OF_CELL / 2;
				if (isLeft) {
					if ((intersectedBound.y - WeekPanel.BORDER_WIDTH) / WeekPanel.WIDTH_OF_CELL == 0) {
						moveToOldLocation();
					} else {
						bound.x = intersectedBound.x - WeekPanel.WIDTH_OF_CELL;
						bound.y = intersectedBound.y;
					}
				}
				
				boolean isRight = x - intersectedBound.x > 0 && x - intersectedBound.x > WeekPanel.WIDTH_OF_CELL / 2;
				if (isRight) {
					if ((intersectedBound.y - WeekPanel.BORDER_WIDTH) / WeekPanel.WIDTH_OF_CELL == 6) {
						moveToOldLocation();
					} else {
						bound.x = intersectedBound.x + WeekPanel.WIDTH_OF_CELL;
						bound.y = intersectedBound.y;
					}
				}
				
				break;
				
			// move to old location when intersect with more than 1 label
			default:
				moveToOldLocation();
				break;
			}
			
			// check for intersect with boundary of grid
			if (bound.x < WeekPanel.BORDER_WIDTH) {
				bound.x = WeekPanel.BORDER_WIDTH;
			} else if (bound.x >= WeekPanel.BORDER_WIDTH + 7 * WeekPanel.WIDTH_OF_CELL) {
				bound.x = WeekPanel.BORDER_WIDTH + 6 * WeekPanel.WIDTH_OF_CELL;
			} 
			
			if (bound.y < WeekPanel.BORDER_HEIGHT) {
				bound.y = WeekPanel.BORDER_HEIGHT;
			} else if (bound.y + bound.height > WeekPanel.BORDER_HEIGHT + 24 * WeekPanel.HEIGHT_OF_CELL) {
				bound.y = WeekPanel.BORDER_HEIGHT + 24 * WeekPanel.HEIGHT_OF_CELL - bound.height;
			}
			
			// check for intersect again
			setBounds(bound);
			for (Rectangle rect : WeekPanel.getLabelsBound(bound)) {
				if (rect.intersects(bound)) {
					moveToOldLocation();
					break;
				}
			}
			
			// update UI
			changeDisplayText();
			updateWeekPanel();
		}
	};
}
